今天我想來點... Hello World!
又是快樂 Debug 的好日子呢!
昨天已經把程式跑起來了,
但又發現了很多奇怪的事情。
HEX Format 每個欄位到底代表什麼?
為什麼 Program Loader 跟 ELF 格式的開始位置對不起來?
真的要 * 16
嗎?
今天就來嘗試解開這些謎團吧!
可惜 HEX Format 的資料太零散,找到的原檔連結也都失效,
只有第二手資訊真的不好整理,
找了很久 02
和 03
格式的用法還不夠確定,
歡迎熟悉的朋友幫忙補充。
因為 Intel 官方和 Wikipedia 上面的運算都沒寫清楚,
我們參考 arm 的文件 HEX File Format,
很明顯地跟程式寫的一樣,每一行都分成 00、02、03、04、05等 Type。
檔案中包含了程式開始位置、資料位置以及內容等資訊。
HEX File 單行格式:
:llaaaatt[dd...]cc
:
每一行 HEX 格式的第一個字元固定都是這個ll
這一行的 [dd...] 有多少 byteaaaa
代表接下來的資料從哪個 address 開始tt
這一行後面的內容代表什麼[dd...]
這一行所帶的資料cc
用來驗證前面內容是否無誤的 checksumHEX File 單行資料的意義是由 tt
欄位決定:
00
Data Record:10 0054 00 130101FD2326810213040103232EA4FC B2
10
代表有 16 byte 的資料0054
資料位置從 0x0054
開始00
這行的資料為 Data Record130101FD2326810213040103232EA4FC
16 byte 資料01
End-of-File (EOF) Record:00000001FF
02
Extended Segment Address Record:02 0000 02 1000 EC
02
代表有 2 byte 的資料0000
這邊永遠為 002
這行的資料為 Extended Segment Address1000
8086 segment 是 16-byte alignment03
Start Segment Address Record:04 0000 03 100000B8 31
04
代表有 4 byte 的資料0000
這邊永遠為 003
這行的資料為 Start Segment Address Record1000
8086 Code segment (CS)暫存器數值,是 16-byte alignment00B8
8086 Instruction pointer(IP) 暫存器數值04
Extended Address Record:02 0000 04 00FF FB
02
代表有 2 byte 的資料0000
這邊永遠為 002
這行的資料為 Extended Address Record00FF
代表最高的兩個 byte offset,在這邊是 0x00FF0000
05
Start Linear Address Record:04 0000 05 08000121 CD
04
代表有 4 byte 的資料0000
這邊永遠為 005
這行的資料為 Start Linear Address Record08000121
代表載入 8086 的 EIP 暫存器位置,也就是 32 bit Program Counter以下是這次的 hello.c 編譯出來的 binary.hex
可以看到程式起始位置為 0x1000 * 16 + 0x00B8
= 0x1000B8
Extended Address 為 0x1000 * 16
= 0x100000
//binary.hex
type
||
:020000021000EC //extend segment address
:10005400130101FD2326810213040103232EA4FCB2 //data
:10006400232604FE6F0080028327C4FE0327C4FDF9 //data
:100074003307F700B7074000034707002380E70072 //data
:100084008327C4FE938717002326F4FE8327C4FE28 //data
:100094000327C4FDB307F70083C70700E39607FCF3 //data
:1000A40013000000138507000324C1021301010398 //data
:1000B40067800000130101FF23261100232481001F //data
:1000C40013040101B70701001385C70EEFF05FF8B1 //data
:1000D40093070000138507008320C10003248100D7 //data
:0800E400130101016780000017 //data
:0D00EC0068656C6C6F20776F726C6421008A //data
:04000003100000B831 //start segment address
:00000001FF //end of hex file
這下子終於看懂 /* ? */
是什麼意思了!
大概是因為原作者沒用到所以不知道這行在幹嘛XD
//RISC-V-TLM by mariusmm
uint32_t code_segment;
code_segment = stol(line.substr(9, 4), nullptr, 16) * 16; /* ? */
研究一下別人的 8086 assembly code 發現 Extended Segment Address 最終會放到 bp 內,
看起來是 Stack Frame 分隔的 Base Pointer,
有興趣的人可以在該檔案搜尋 "Intel-hex file" ,就可以看到了。
也就是說這兩個分別是起始的 Program Counter 和 Stack Pointer 位置,
但這樣一來,Program Memory Layout 又說不通了,
Stack Section 竟然是在比 Text Section 低的位置?
恩....看起來還要再花時間研究一下,
不過夜深了,猴子也累了,今天就先寫到這裡吧!
hello world 寫起來很簡單,
但是執行的時候卻碰到問題:
沒有看到預期的的字元被一個一個寫進指定的 address。
第一個步驟就是把 hello.o 反組譯之後一條一條指令核對!
Function Call 的 JAL
和 JALR
看起來沒什麼問題,
就來看 printToTrace
的內容:
00010054 <printToTrace>:
printToTrace():
10054: fd010113 addi sp,sp,-48
10058: 02812623 sw s0,44(sp)
1005c: 03010413 addi s0,sp,48
10060: fca42e23 sw a0,-36(s0)
10064: fe042623 sw zero,-20(s0)
10068: 0280006f j 10090 <printToTrace+0x3c>
1006c: fec42783 lw a5,-20(s0)
10070: fdc42703 lw a4,-36(s0)
10074: 00f70733 add a4,a4,a5
10078: 004007b7 lui a5,0x400
1007c: 00074703 lbu a4,0(a4)
10080: 00e78023 sb a4,0(a5) # 400000 <__global_pointer$+0x3ee707>
10084: fec42783 lw a5,-20(s0)
10088: 00178793 addi a5,a5,1
1008c: fef42623 sw a5,-20(s0)
10090: fec42783 lw a5,-20(s0)
10094: fdc42703 lw a4,-36(s0)
10098: 00f707b3 add a5,a4,a5
1009c: 0007c783 lbu a5,0(a5)
100a0: fc0796e3 bnez a5,1006c <printToTrace+0x18>
100a4: 00000013 nop
100a8: 00078513 mv a0,a5
100ac: 02c12403 lw s0,44(sp)
100b0: 03010113 addi sp,sp,48
100b4: 00008067 ret
發現又是 branch 的時候出問題,
它竟然直接跳到下一行 0x10d4
結束 loop,
而不是跳到 0x1006c
執行 loop 內容!
rdValue: 0x3fff0 immValue: 0x30
current_pc: 0x10b4 target_pc: 0x10d4 JALR 1 0 0 rs1Value: 0x10d4 rs2Value: 0x0 r
但是東看西看,前天改 BNE
的沒問題!
往回追一道指令,發現 LBU 拿到 0,
也就是說 "hello world!" 的位置從第一個 byte 就是 0,
那個 address... 是不是怪怪的啊!0x100ec
看起來就跟 HEX File算出來的 main 起始位置 0x10b8
差很遠,
肯定哪裡怪怪的!
P.S.ADD
數值也怪怪的,不過是 Logger 的問題,先不理它!
current_pc: 0x1068 target_pc: 0x1090 JAL 0 8 0 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x28
current_pc: 0x1090 target_pc: 0x1094 LW 8 12 15 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x0 immValue: 0xffffffec
current_pc: 0x1094 target_pc: 0x1098 LW 8 28 14 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x100ec immValue: 0xffffffdc
current_pc: 0x1098 target_pc: 0x109c ADD 14 15 15 rs1Value: 0x100ec rs2Value: 0x100ec rdValue: 0x100ec immValue: 0x0
current_pc: 0x109c target_pc: 0x10a0 LBU 15 0 15 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x0
current_pc: 0x10a0 target_pc: 0x10a4 BNE 15 0 13 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0xffffffcc
假設是 Data Section 拿錯位置,0x10b8
要對起來應該是 0x100b8
,
看起來是該改回 * 16
的時候了!
//memory.cpp
...
case 2: { //Extended segment address
extended_address = std::stoul(line.substr(9, 4), nullptr, 16) * 16;
}
break;
case 3: { //Start segment address
uint32_t code_segment = stoul(line.substr(9, 4), nullptr, 16) * 16;
...
Bingo!!!
果然是它!
前幾天沒有找到正確資料的報應立刻就來了。
之後好好地把 HEX Format Program Loader 修好吧!
github 頁面 Tag: ITDay30
先在 Bus 裡面加了一個簡單的 console,
使用 Memory Mapped I/O 做法,
只要存取指定的 address space 就是存取指定的 I/O device。
本來打算另外寫一篇完整一點的 External Device 章節,
但時間不太夠,只能之後再補了。
//bus.cpp
...
switch (addr) {
case CONSOLE_BASE:
std::cout << "console: \'" << *reinterpret_cast<unsigned char*>(&data) << "\'" << std::endl;
break;
default:
memory_socket->b_transport(trans, delay);
break;
}
...
//bus.h
...
enum memoryMapp {
MEMORY_BASE = 0x000000,
CONSOLE_BASE = 0x400000,
};
...
程式非常簡單,
就是把 "hello world!"
依序寫入上面設定的 address。
//test.c
#define CONSOLE (*(unsigned char *)0x400000)
int printToTrace(char* input)
{
int i=0;
while(input[i] != '\0') {
CONSOLE = input[i];
i++;
}
}
int main()
{
printToTrace("hello world!");
return 0;
}
current_pc: 0x100b8 target_pc: 0x100bc ADDI 2 16 2 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x3fff0 immValue: 0xfffffff0
current_pc: 0x100bc target_pc: 0x100c0 SW 2 1 12 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x0 immValue: 0xc
current_pc: 0x100c0 target_pc: 0x100c4 SW 2 8 8 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x8
current_pc: 0x100c4 target_pc: 0x100c8 ADDI 2 16 8 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x40000 immValue: 0x10
current_pc: 0x100c8 target_pc: 0x100cc LUI 2 0 15 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x10000 immValue: 0x10
current_pc: 0x100cc target_pc: 0x100d0 ADDI 15 12 10 rs1Value: 0x10000 rs2Value: 0x0 rdValue: 0x100ec immValue: 0xec
current_pc: 0x100d0 target_pc: 0x10054 JAL 31 5 1 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x100d4 immValue: 0xffffff84
current_pc: 0x10054 target_pc: 0x10058 ADDI 2 16 2 rs1Value: 0x3ffc0 rs2Value: 0x0 rdValue: 0x3ffc0 immValue: 0xffffffd0
current_pc: 0x10058 target_pc: 0x1005c SW 2 8 12 rs1Value: 0x3ffc0 rs2Value: 0x40000 rdValue: 0x0 immValue: 0x2c
current_pc: 0x1005c target_pc: 0x10060 ADDI 2 16 8 rs1Value: 0x3ffc0 rs2Value: 0x0 rdValue: 0x3fff0 immValue: 0x30
current_pc: 0x10060 target_pc: 0x10064 SW 8 10 28 rs1Value: 0x3fff0 rs2Value: 0x100ec rdValue: 0x0 immValue: 0xffffffdc
current_pc: 0x10064 target_pc: 0x10068 SW 8 0 12 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x0 immValue: 0xffffffec
current_pc: 0x10068 target_pc: 0x10090 JAL 0 8 0 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x28
current_pc: 0x10090 target_pc: 0x10094 LW 8 12 15 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x0 immValue: 0xffffffec
current_pc: 0x10094 target_pc: 0x10098 LW 8 28 14 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x100ec immValue: 0xffffffdc
current_pc: 0x10098 target_pc: 0x1009c ADD 14 15 15 rs1Value: 0x100ec rs2Value: 0x100ec rdValue: 0x100ec immValue: 0x0
current_pc: 0x1009c target_pc: 0x100a0 LBU 15 0 15 rs1Value: 0x68 rs2Value: 0x0 rdValue: 0x68 immValue: 0x0
current_pc: 0x100a0 target_pc: 0x1006c BNE 15 0 13 rs1Value: 0x68 rs2Value: 0x0 rdValue: 0x0 immValue: 0xffffffcc
current_pc: 0x1006c target_pc: 0x10070 LW 8 12 15 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x0 immValue: 0xffffffec
current_pc: 0x10070 target_pc: 0x10074 LW 8 28 14 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x100ec immValue: 0xffffffdc
current_pc: 0x10074 target_pc: 0x10078 ADD 14 15 14 rs1Value: 0x100ec rs2Value: 0x0 rdValue: 0x100ec immValue: 0x0
current_pc: 0x10078 target_pc: 0x1007c LUI 0 4 15 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x400000 immValue: 0x400
current_pc: 0x1007c target_pc: 0x10080 LBU 14 0 14 rs1Value: 0x68 rs2Value: 0x0 rdValue: 0x68 immValue: 0x0
console: h
...
console: e
...
console: l
...
console: l
...
console: o
...
console:
...
console: w
...
console: o
...
console: r
...
console: l
...
console: d
...
console: !
current_pc: 0x10080 target_pc: 0x10084 SB 15 14 0 rs1Value: 0x400000 rs2Value: 0x21 rdValue: 0x0 immValue: 0x0
current_pc: 0x10084 target_pc: 0x10088 LW 8 12 15 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0xb immValue: 0xffffffec
current_pc: 0x10088 target_pc: 0x1008c ADDI 15 1 15 rs1Value: 0xc rs2Value: 0x0 rdValue: 0xc immValue: 0x1
current_pc: 0x1008c target_pc: 0x10090 SW 8 15 12 rs1Value: 0x3fff0 rs2Value: 0xc rdValue: 0x0 immValue: 0xffffffec
current_pc: 0x10090 target_pc: 0x10094 LW 8 12 15 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0xc immValue: 0xffffffec
current_pc: 0x10094 target_pc: 0x10098 LW 8 28 14 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x100ec immValue: 0xffffffdc
current_pc: 0x10098 target_pc: 0x1009c ADD 14 15 15 rs1Value: 0x100ec rs2Value: 0x100f8 rdValue: 0x100f8 immValue: 0x0
current_pc: 0x1009c target_pc: 0x100a0 LBU 15 0 15 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x0
current_pc: 0x100a0 target_pc: 0x100a4 BNE 15 0 13 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0xffffffcc
current_pc: 0x100a4 target_pc: 0x100a8 ADDI 0 0 0 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x0
current_pc: 0x100a8 target_pc: 0x100ac ADDI 15 0 10 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x0
current_pc: 0x100ac target_pc: 0x100b0 LW 2 12 8 rs1Value: 0x3ffc0 rs2Value: 0x0 rdValue: 0x40000 immValue: 0x2c
current_pc: 0x100b0 target_pc: 0x100b4 ADDI 2 16 2 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x3fff0 immValue: 0x30
current_pc: 0x100b4 target_pc: 0x100d4 JALR 1 0 0 rs1Value: 0x100d4 rs2Value: 0x0 rdValue: 0x0 immValue: 0x0
current_pc: 0x100d4 target_pc: 0x100d8 ADDI 0 0 15 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x0
current_pc: 0x100d8 target_pc: 0x100dc ADDI 15 0 10 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x0
current_pc: 0x100dc target_pc: 0x100e0 LW 2 12 1 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x0 immValue: 0xc
current_pc: 0x100e0 target_pc: 0x100e4 LW 2 8 8 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x8
current_pc: 0x100e4 target_pc: 0x100e8 ADDI 2 16 2 rs1Value: 0x40000 rs2Value: 0x0 rdValue: 0x40000 immValue: 0x10
current_pc: 0x100e8 target_pc: 0x0 JALR 1 0 0 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x0
INVALID: Opcode :0
Illegal Instruction, end simulation!
exception!
Info: /OSCI/SystemC: Simulation stopped by user.
花了 30 天,終於寫出自己的 hello world 了。
最初的最初那篇,就是在哈囉。
只是踏著前人的足跡,用著前人留下的工具。
最後的最後這篇,還是在哈囉。
但這次不一樣,
我們有了自己建立的工具,
還多了看著這篇文章的你我 :-D